﻿using iTextSharp.text;
using iTextSharp.text.pdf;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using Image = iTextSharp.text.Image;

namespace Common
{
    public class PDFHelper
    {
        /// <summary>
        /// 压缩PDF文件里的图片
        /// </summary>
        /// <param name="sourceFile"></param>
        /// <param name="TargetFile"></param>
        public static void CompressPDFFile(string sourceFile, string TargetFile)
        {
            if (new FileInfo(sourceFile).Length <= 5 * 1024 * 1024)
            {
                File.Copy(sourceFile, TargetFile);
            }
            else
            {
                try
                {
                    PdfReader reader2 = new PdfReader(sourceFile);
                    //输出文件流.
                    using (FileStream fs = new FileStream(TargetFile, FileMode.Create, FileAccess.Write, FileShare.None))
                    {
                        using (PdfStamper stamper = new PdfStamper(reader2, fs))
                        {
                            PdfDictionary page;

                            //Get the page count
                            int pageCount2 = reader2.NumberOfPages;
                            //Loop through each page
                            for (int i = 1; i <= pageCount2; i++)
                            {
                                //Get the page
                                page = reader2.GetPageN(i);
                                var pdfObjects = FindImageInPDFDictionarys(page, DistinguishImageIsNeedCompress);
                                foreach (var obj in pdfObjects)
                                {
                                    if (obj != null)
                                    {
                                        iTextSharp.text.Image img = ResizedImage(obj, reader2);
                                        iTextSharp.text.Image maskImage = img.ImageMask;
                                        PdfReader.KillIndirect(obj);//移除老图片,只是移除了关联.
                                        if (maskImage != null)
                                            stamper.Writer.AddDirectImageSimple(maskImage); //把新图片写进去
                                        if (img != null)
                                            stamper.Writer.AddDirectImageSimple(img, (PRIndirectReference)obj); //把新图片写进去.
                                    }
                                }
                            }

                        }
                    }
                }
                catch (Exception ex)
                {
                    File.Copy(sourceFile, TargetFile, true);
                }
            }
        }

        /// <summary>
        /// Jpg转换Pdf
        /// </summary>
        /// <param name="jpgFile"></param>
        /// <param name="pdf"></param>
        public static void ConvertJPG2PDF(string jpgFile, string pdf)
        {
            //PDF纸张为A4横向
            iTextSharp.text.Rectangle rec = PageSize.ARCH_A.Rotate();
            var document = new Document(rec);
            using (var stream = new FileStream(pdf, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                PdfWriter.GetInstance(document, stream);
                document.Open();
                //先将图片限制到500K左右，再转换为PDF
                using (var imageStream = ZipJpgFile(jpgFile, ImageFormat.Jpeg, 500))
                {
                    var image = Image.GetInstance(imageStream);
                    if (image.Height > rec.Height)
                    {
                        image.ScaleToFit(rec.Width, rec.Height);
                    }
                    else if (image.Width > rec.Width)
                    {
                        image.ScaleToFit(rec.Width, rec.Height);
                    }
                    image.Alignment = Image.ALIGN_CENTER;
                    document.Add(image);
                }

                document.Close();
            }
        }

        public static Bitmap BitmapToGrayscale(Bitmap source)
        {
            // Create target image.
            int width = source.Width;
            int height = source.Height;
            Bitmap target = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            // Set the palette to discrete shades of gray
            ColorPalette palette = target.Palette;
            for (int i = 0; i < palette.Entries.Length; i++)
            {
                palette.Entries[i] = Color.FromArgb(0, i, i, i);
            }
            target.Palette = palette;

            // Lock bits so we have direct access to bitmap data
            BitmapData targetData = target.LockBits(new System.Drawing.Rectangle(0, 0, width, height),
                ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
            BitmapData sourceData = source.LockBits(new System.Drawing.Rectangle(0, 0, width, height),
                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            //unsafe
            //{
            //    for (int r = 0; r < height; r++)
            //    {
            //        byte* pTarget = (byte*)(targetData.Scan0 + r * targetData.Stride);
            //        byte* pSource = (byte*)(sourceData.Scan0 + r * sourceData.Stride);
            //        for (int c = 0; c < width; c++)
            //        {
            //            byte colorIndex = (byte)(((*pSource) * 0.3 + *(pSource + 1) * 0.59 + *(pSource + 2) * 0.11));
            //            *pTarget = colorIndex;
            //            pTarget++;
            //            pSource += 3;
            //        }
            //    }
            //}

            target.UnlockBits(targetData);
            source.UnlockBits(sourceData);
            return target;
        }

        #region 私有方法
        /// <summary>
        /// 压缩图片至n Kb以下
        /// </summary>
        /// <param name="fileName">图片路径</param>
        /// <param name="format">图片格式</param>
        /// <param name="targetLen">压缩后大小(KB)</param>
        /// <returns>压缩后的图片内存流</returns>
        private static MemoryStream ZipJpgFile(string fileName, ImageFormat format, long targetLen)
        {
            System.Drawing.Image img = System.Drawing.Image.FromFile(fileName);
            long srcLen = new FileInfo(fileName).Length;

            //设置允许大小偏差幅度 默认10kb
            const long nearlyLen = 10240;

            //返回内存流  如果参数中原图大小没有传递 则使用内存流读取
            var ms = new MemoryStream();
            if (0 == srcLen)
            {
                img.Save(ms, format);
                srcLen = ms.Length;
            }

            //单位 由Kb转为byte 若目标大小高于原图大小，则满足条件退出
            targetLen *= 1024;
            if (targetLen >= srcLen)
            {
                ms.SetLength(0);
                ms.Position = 0;
                img.Save(ms, format);
                return ms;
            }

            //获取目标大小最低值
            var exitLen = targetLen - nearlyLen;

            //初始化质量压缩参数 图像 内存流等
            var quality = (long)Math.Floor(100.00 * targetLen / srcLen);
            var parms = new EncoderParameters(1);

            //获取编码器信息
            ImageCodecInfo formatInfo = null;
            var encoders = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo icf in encoders)
            {
                if (icf.FormatID == format.Guid)
                {
                    formatInfo = icf;
                    break;
                }
            }

            //使用二分法进行查找 最接近的质量参数
            long startQuality = quality;
            long endQuality = 100;
            quality = (startQuality + endQuality) / 2;

            while (true)
            {
                //设置质量
                parms.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);

                //清空内存流 然后保存图片
                ms.SetLength(0);
                ms.Position = 0;
                img.Save(ms, formatInfo, parms);

                //若压缩后大小低于目标大小，则满足条件退出
                if (ms.Length >= exitLen && ms.Length <= targetLen)
                {
                    break;
                }
                else if (startQuality >= endQuality) //区间相等无需再次计算
                {
                    break;
                }
                else if (ms.Length < exitLen) //压缩过小,起始质量右移
                {
                    startQuality = quality;
                }
                else //压缩过大 终止质量左移
                {
                    endQuality = quality;
                }

                //重新设置质量参数 如果计算出来的质量没有发生变化，则终止查找。这样是为了避免重复计算情况{start:16,end:18} 和 {start:16,endQuality:17}
                var newQuality = (startQuality + endQuality) / 2;
                if (newQuality == quality)
                {
                    break;
                }
                quality = newQuality;
            }
            ms.Position = 0;
            return ms;
        }

        /// <summary>
        /// 判断是否PDF文件
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool IsPDF(string filePath)
        {
            try
            {
                return Path.GetExtension(filePath).ToLower().Contains("pdf");
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        //在pdf页面中 找到图片
        private static List<PdfObject> FindImageInPDFDictionarys(PdfDictionary pg, DistinguishImage distinguishMethod)
        {
            List<PdfObject> pdfObjects = new List<PdfObject>();
            try
            {
                PdfDictionary res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
                PdfDictionary xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
                if (xobj != null)
                {
                    foreach (PdfName name in xobj.Keys)
                    {
                        Console.WriteLine(name.ToString());
                        PdfObject obj = xobj.Get(name);
                        if (obj.IsIndirect())
                        {
                            PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
                            PdfName type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
                            //image at the root of the pdf
                            if (PdfName.IMAGE.Equals(type))
                            {
                                if (distinguishMethod(tg) == true)
                                {
                                    pdfObjects.Add(obj);
                                }
                                else
                                {
                                    continue;//继续找
                                }
                            }
                            //// image inside a form
                            //else if (PdfName.FORM.Equals(type))
                            //{
                            //    return FindImageInPDFDictionarys(tg, distinguishMethod);
                            //}
                            ////image inside a group
                            //else if (PdfName.GROUP.Equals(type))
                            //{
                            //    return FindImageInPDFDictionarys(tg, distinguishMethod);
                            //}
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                pdfObjects = new List<PdfObject>();
            }
            return pdfObjects;
        }

        /// <summary>
        /// 辨别图片的委托
        /// </summary>
        /// <param name="imgObject"></param>
        /// <returns></returns>
        delegate bool DistinguishImage(PdfDictionary imgObject);

        /// <summary>
        /// 辨别图片是不是需要压缩
        /// </summary>
        /// <param name="imgObject"></param>
        /// <returns></returns>
        private static bool DistinguishImageIsNeedCompress(PdfDictionary imgObject)
        {
            //int width, height, length;
            //int.TryParse(imgObject.Get(PdfName.WIDTH).ToString(), out width);
            //int.TryParse(imgObject.Get(PdfName.HEIGHT).ToString(), out height);
            //int.TryParse(imgObject.Get(PdfName.LENGTH).ToString(), out length);

            ////从这3个参数就可以判断是不是需要压缩.
            //if (width == 270 && height == 111 && length == 11878)
            //{
            //    return true;
            //}
            //else
            //{
            //    return false;
            //}
            return true;
        }

        private static iTextSharp.text.Image ResizedImage(PdfObject obj, PdfReader reader)
        {
            if (obj != null)
            {
                int XrefIndex =
                    Convert.ToInt32(((PRIndirectReference)obj).Number.ToString(System.Globalization.CultureInfo
                        .InvariantCulture));
                PdfObject pdfObj = reader.GetPdfObject(XrefIndex);
                PdfStream pdfStrem = (PdfStream)pdfObj;
                byte[] bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStrem);
                if ((bytes != null))
                {
                    using (System.IO.MemoryStream memStream = new System.IO.MemoryStream(bytes))
                    {
                        memStream.Position = 0;
                        System.Drawing.Image img = ImageHelper.ResizeImage(System.Drawing.Image.FromStream(memStream), .4M);
                        ImageFormat format = img.PixelFormat == PixelFormat.Format1bppIndexed
                                             || img.PixelFormat == PixelFormat.Format4bppIndexed
                                             || img.PixelFormat == PixelFormat.Format8bppIndexed
                            ? ImageFormat.Tiff
                            : ImageFormat.Jpeg;

                        var pdfImage = iTextSharp.text.Image.GetInstance(img, format);
                        return pdfImage;
                    }
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }
        #endregion
    }

    public partial class ImageHelper
    {
        /// <summary>
        /// Resize the image to the specified width and height.
        /// </summary>
        /// <param name="image">The image to resize.</param>
        /// <param name="width">The width to resize to.</param>
        /// <param name="height">The height to resize to.</param>
        /// <returns>The resized image.</returns>
        public static Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
        {
            var destRect = new System.Drawing.Rectangle(0, 0, width, height);
            var destImage = new System.Drawing.Bitmap(width, height);

            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (var graphics = Graphics.FromImage(destImage))
            {
                graphics.CompositingMode = CompositingMode.SourceCopy;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                using (var wrapMode = new ImageAttributes())
                {
                    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
                }
            }

            return destImage;
        }

        public static Bitmap ResizeImage(System.Drawing.Image image, decimal percentage)
        {
            int baseSize = image.Height;
            if (image.Width < image.Height)
            {
                baseSize = image.Width;
            }

            int width = image.Width;
            int height = image.Height;

            if (baseSize > 1080)
            {
                width = (int)Math.Round(image.Width * percentage, MidpointRounding.AwayFromZero);
                height = (int)Math.Round(image.Height * percentage, MidpointRounding.AwayFromZero);
            }

            return ResizeImage(image, width, height);
        }
    }

}
